﻿using HDS.Common.Helper;
using HDS.Data;
using HDS.DistributorApi.Core;
using HDS.IService.DistributorApi;
using HDS.IService.Log;
using HDS.Model;
using HDS.Model.Entity.Log;
using HDS.Model.Entity.Master;
using HDS.Model.Entity.View;
using HDS.Service.Master;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using LogLevel = HDS.Model.LogLevel;

namespace HDS.Service.DistributorApi
{
    public class DistributorApiService : IDistributorApiService
    {
        private MasterDbContext dbContext = new MasterDbContext();
        private LogDbContext logDbContext = new LogDbContext();

        private DistributorConfig distributorConfig;
        private IDistributorPushApi idistributorPushApi;

        private readonly ILogger<DistributorApiService> _ILogger;
        private readonly IApiLogService _IApiLogService;

        public DistributorApiService(ILogger<DistributorApiService> ilogger, IApiLogService iapiLogService)
        {
            _ILogger = ilogger;
            _IApiLogService = iapiLogService;
        }

        public async Task InitConfig(string distributor)
        {
            distributorConfig = await dbContext.DistributorConfig.GetSingleAsync(e => e.Distributor.Equals(distributor));
            if (distributorConfig == null)
                throw new NullReferenceException("Config is null");

            if (distributorConfig.DistributorGroup == "alitrip")
            {
                idistributorPushApi = new HDS.DistributorApi.Alitrip.DistributorPushApi(_ILogger, _IApiLogService);
            }
            else if (distributorConfig.DistributorGroup == "ctrip")
            {
                idistributorPushApi = new HDS.DistributorApi.Ctrip.DistributorPushApi(_ILogger, _IApiLogService);
            }
            else if (distributorConfig.DistributorGroup == "qunar")
            {
                idistributorPushApi = new HDS.DistributorApi.Qunar.DistributorPushApi(_ILogger, _IApiLogService);
            }
            else if (distributorConfig.DistributorGroup == "meituan")
            {
                idistributorPushApi = new HDS.DistributorApi.Meituan.DistributorPushApi(_ILogger, _IApiLogService);
            }

            if (idistributorPushApi == null)
                throw new NullReferenceException("IDistributorPushApi is null");

            await idistributorPushApi.LoadConfig(distributorConfig);
        }

        public async Task<ResultData<string>> PushConfirmRoom(string distributorOrderId, string orderId, int updatedBy, string updatedByName)
        {
            if (distributorConfig == null)
                throw new NullReferenceException("Config is null");

            var resultData = await idistributorPushApi.PushConfirmRoom(distributorConfig.Distributor, distributorOrderId, orderId);
            var logText = "";
            if (resultData.Success)
            {
                //更新分销商订单状态
                await dbContext.OrderHotelDistributor.UpdateAsync(e => new OrderHotelDistributor { DistributorOrderStatus = DistributorOrderStatus.Confirmed }, e => e.OrderId.Equals(orderId));

                logText = $"确认有房[{distributorConfig.Distributor}], 推送成功";
                await logDbContext.UpdateLog.InsertAsync(new UpdateLog
                {
                    LogType = UpdateLogType.Order,
                    LogSubType = "PushConfirmRoom",
                    LogText = logText,
                    LogLevel = LogLevel.Information,
                    BusinessId = orderId,
                    UpdatedBy = updatedBy,
                    UpdatedByName = updatedByName,
                    UpdatedTime = DateTime.Now,
                });
            }
            else
            {
                logText = $"确认有房[{distributorConfig.Distributor}], 推送失败: {resultData.Message}";
                await logDbContext.UpdateLog.InsertAsync(new UpdateLog
                {
                    LogType = UpdateLogType.Order,
                    LogSubType = "PushConfirmRoom",
                    LogText = logText,
                    LogLevel = LogLevel.Error,
                    BusinessId = orderId,
                    UpdatedBy = updatedBy,
                    UpdatedByName = updatedByName,
                    UpdatedTime = DateTime.Now,
                });
            }
            resultData.Message = logText;
            return resultData;
        }

        public async Task<ResultData<string>> PushHotelConfirmNo(string distributorOrderId, string orderId, string hotelConfirmNo, int updatedBy, string updatedByName)
        {
            if (distributorConfig == null)
                throw new NullReferenceException("Config is null");

            var resultData = await idistributorPushApi.PushHotelConfirmNo(distributorConfig.Distributor, distributorOrderId, orderId, hotelConfirmNo);
            var logText = "";
            if (resultData.Success)
            {
                logText = $"酒店确认号[{hotelConfirmNo}]推送成功{distributorConfig.Distributor}";
                await logDbContext.UpdateLog.InsertAsync(new UpdateLog
                {
                    LogType = UpdateLogType.Order,
                    LogSubType = "PushHotelConfirmNo",
                    LogText = logText,
                    LogLevel = LogLevel.Information,
                    BusinessId = orderId,
                    UpdatedBy = updatedBy,
                    UpdatedByName = updatedByName,
                    UpdatedTime = DateTime.Now,
                });
            }
            else
            {
                logText = $"酒店确认号[{hotelConfirmNo}]推送失败{distributorConfig.Distributor}: {resultData.Message}";
                await logDbContext.UpdateLog.InsertAsync(new UpdateLog
                {
                    LogType = UpdateLogType.Order,
                    LogSubType = "PushHotelConfirmNo",
                    LogText = logText,
                    LogLevel = LogLevel.Error,
                    BusinessId = orderId,
                    UpdatedBy = updatedBy,
                    UpdatedByName = updatedByName,
                    UpdatedTime = DateTime.Now,
                });
            }
            resultData.Message = logText;
            return resultData;
        }

        public async Task<ResultData<string>> PushHotelInfo(int hotelId, bool compareMd5, int updatedBy, string updatedByName)
        {
            if (distributorConfig == null)
                throw new NullReferenceException("Config is null");

            var vdHotelInfo = await dbContext.Db.SqlQueryable<VDistributorHotel>(DistributorService.VDistributorHotelSQL).Where(e => e.Distributor.Equals(distributorConfig.Distributor) && e.HotelId.Equals(hotelId)).FirstAsync();
            if(vdHotelInfo == null)
                throw new NullReferenceException("VDistributorHotel is null");

            if (compareMd5 && vdHotelInfo.HotelMd5.Equals(vdHotelInfo.HotelMd5New))
            {
                return new ResultData<string> { Success = false, Code = ErrorCode.NotPushed, Message = "Information has not changed" };
            }

            var resultData = await idistributorPushApi.PushHotelInfo(distributorConfig.Distributor, vdHotelInfo);
            var logText = "";
            if (resultData.Success)
            {
                logText = $"酒店信息推送成功 {distributorConfig.Distributor}";
                await logDbContext.UpdateLog.InsertAsync(new UpdateLog
                {
                    LogType = UpdateLogType.StaticData,
                    LogSubType = distributorConfig.Distributor,
                    LogText = logText,
                    LogLevel = LogLevel.Information,
                    BusinessId = hotelId.ToString(),
                    UpdatedBy = updatedBy,
                    UpdatedByName = updatedByName,
                    UpdatedTime = DateTime.Now,
                });
            }
            else
            {
                if (resultData.Code != ErrorCode.NotPushed)
                {
                    logText = $"酒店信息推送失败 {distributorConfig.Distributor}: {resultData.Message}";
                    await logDbContext.UpdateLog.InsertAsync(new UpdateLog
                    {
                        LogType = UpdateLogType.StaticData,
                        LogSubType = distributorConfig.Distributor,
                        LogText = logText,
                        LogLevel = LogLevel.Error,
                        BusinessId = hotelId.ToString(),
                        UpdatedBy = updatedBy,
                        UpdatedByName = updatedByName,
                        UpdatedTime = DateTime.Now,
                    });
                }
            }

            if (resultData.Data != null)
            {
                await dbContext.DistributorHotel.UpdateAsync(e => new DistributorHotel
                {
                    DistributorHotelId = resultData.Data.DistributorHotelId,
                    DistributorHotelId1 = resultData.Data.DistributorHotelId1,
                    PushResult = resultData.Data.PushResult,
                    PushStatus = resultData.Data.PushStatus,
                    PushError = resultData.Data.PushError,
                    UpdatedBy = updatedBy,
                    UpdatedByName = updatedByName,
                    UpdatedTime = DateTime.Now,
                },
                e => e.Distributor.Equals(distributorConfig.Distributor) && e.HotelId.Equals(hotelId));
            }

            return new ResultData<string> { Success = resultData.Success, Code = resultData.Code, Message = logText };
        }

        public async Task<ResultData<string>> PushDeleteHotel(int hotelId, int updatedBy, string updatedByName)
        {
            if (distributorConfig == null)
                throw new NullReferenceException("Config is null");

            var vdHotelInfo = await dbContext.Db.SqlQueryable<VDistributorHotel>(DistributorService.VDistributorHotelSQL).Where(e => e.Distributor.Equals(distributorConfig.Distributor) && e.HotelId.Equals(hotelId)).FirstAsync();
            if (vdHotelInfo == null)
                throw new NullReferenceException("VDistributorHotel is null");

            var resultData = await idistributorPushApi.PushDeleteHotel(distributorConfig.Distributor, vdHotelInfo);
            var logText = "";
            if (resultData.Success)
            {
                logText = $"删除酒店推送成功 {distributorConfig.Distributor}";
                await logDbContext.UpdateLog.InsertAsync(new UpdateLog
                {
                    LogType = UpdateLogType.StaticData,
                    LogSubType = distributorConfig.Distributor,
                    LogText = logText,
                    LogLevel = LogLevel.Information,
                    BusinessId = hotelId.ToString(),
                    UpdatedBy = updatedBy,
                    UpdatedByName = updatedByName,
                    UpdatedTime = DateTime.Now,
                });
            }
            else
            {
                logText = $"删除酒店推送失败 {distributorConfig.Distributor}: {resultData.Message}";
                await logDbContext.UpdateLog.InsertAsync(new UpdateLog
                {
                    LogType = UpdateLogType.StaticData,
                    LogSubType = distributorConfig.Distributor,
                    LogText = logText,
                    LogLevel = LogLevel.Error,
                    BusinessId = hotelId.ToString(),
                    UpdatedBy = updatedBy,
                    UpdatedByName = updatedByName,
                    UpdatedTime = DateTime.Now,
                });
            }

            if (resultData.Data != null)
            {
                await dbContext.DistributorHotel.UpdateAsync(e => new DistributorHotel
                {
                    DistributorHotelId = resultData.Data.DistributorHotelId,
                    DistributorHotelId1 = resultData.Data.DistributorHotelId1,
                    PushResult = resultData.Data.PushResult,
                    PushStatus = resultData.Data.PushStatus,
                    PushError = resultData.Data.PushError,
                    UpdatedBy = updatedBy,
                    UpdatedByName = updatedByName,
                    UpdatedTime = DateTime.Now,
                },
                e => e.Distributor.Equals(distributorConfig.Distributor) && e.HotelId.Equals(hotelId));

                await dbContext.DistributorRoom.DeleteAsync(e => e.Distributor.Equals(distributorConfig.Distributor) && e.HotelId.Equals(hotelId));
            }

            return new ResultData<string> { Success = resultData.Success, Code = resultData.Code, Message = logText };
        }

        public async Task<ResultData<string>> PushRoomInfo(int hotelId, bool compareMd5, int updatedBy, string updatedByName)
        {
            if (distributorConfig == null)
                throw new NullReferenceException("Config is null");

            var vdHotelInfo = await dbContext.Db.SqlQueryable<VDistributorHotel>(DistributorService.VDistributorHotelSQL).Where(e => e.Distributor.Equals(distributorConfig.Distributor) && e.HotelId.Equals(hotelId)).FirstAsync();
            if (vdHotelInfo == null)
                throw new NullReferenceException("VDistributorHotel is null");

            var sql = DistributorService.VDistributorRoomSQL;
            sql += $" AND dr.distributor = '{distributorConfig.Distributor}'";
            sql += $" WHERE r.hotel_id = {hotelId}";
            var vdRoomList = await dbContext.Db.SqlQueryable<VDistributorRoom>(sql).ToListAsync();
            if (vdRoomList.IsEmpty())
                return new ResultData<string> { Success = false, Code = ErrorCode.DataValidationFailed, Message = "Rooms is null" };

            if (compareMd5)
            {
                vdRoomList = vdRoomList.Where(e => !e.RoomMd5.Equals(e.RoomMd5New)).ToList();
                if (vdRoomList.IsEmpty())
                    return new ResultData<string> { Success = false, Code = ErrorCode.NotPushed, Message = "Information has not changed" };
            }

            var resultData = await idistributorPushApi.PushRoomInfo(distributorConfig.Distributor, vdRoomList);
            var logText = "";
            if (resultData.Success)
            {
                logText = $"房型信息推送成功 {distributorConfig.Distributor}";
                await logDbContext.UpdateLog.InsertAsync(new UpdateLog
                {
                    LogType = UpdateLogType.StaticData,
                    LogSubType = distributorConfig.Distributor,
                    LogText = logText,
                    LogLevel = LogLevel.Information,
                    BusinessId = hotelId.ToString(),
                    UpdatedBy = updatedBy,
                    UpdatedByName = updatedByName,
                    UpdatedTime = DateTime.Now,
                });
            }
            else
            {
                if (resultData.Code != ErrorCode.NotPushed)
                {
                    logText = $"房型信息推送失败 {distributorConfig.Distributor}: {resultData.Message}";
                    await logDbContext.UpdateLog.InsertAsync(new UpdateLog
                    {
                        LogType = UpdateLogType.StaticData,
                        LogSubType = distributorConfig.Distributor,
                        LogText = logText,
                        LogLevel = LogLevel.Error,
                        BusinessId = hotelId.ToString(),
                        UpdatedBy = updatedBy,
                        UpdatedByName = updatedByName,
                        UpdatedTime = DateTime.Now,
                    });
                }
            }

            if (!resultData.Data.IsEmpty())
            {
                foreach (var vdRoom in resultData.Data)
                {
                    var old = await dbContext.DistributorRoom.GetSingleAsync(e => e.Distributor.Equals(distributorConfig.Distributor) && e.HotelId.Equals(hotelId) && e.RoomId.Equals(vdRoom.RoomId));

                    if (old != null)
                    {
                        await dbContext.DistributorRoom.UpdateAsync(e => new DistributorRoom
                        {
                            DistributorRoomId = vdRoom.DistributorRoomId.NullToString(),
                            DistributorRoomId1 = vdRoom.DistributorRoomId1.NullToString(),
                            PushResult = vdRoom.PushResult,
                            PushStatus = vdRoom.PushStatus,
                            PushError = vdRoom.PushError,
                            UpdatedBy = updatedBy,
                            UpdatedByName = updatedByName,
                            UpdatedTime = DateTime.Now,
                        },
                        e => e.Distributor.Equals(distributorConfig.Distributor) && e.HotelId.Equals(hotelId) && e.RoomId.Equals(vdRoom.RoomId));
                    }
                    else
                    {
                        await dbContext.DistributorRoom.InsertAsync(new DistributorRoom { 
                            HotelId = hotelId,
                            RoomId = vdRoom.RoomId,
                            RoomMd5 = vdRoom.RoomMd5New,
                            Distributor = distributorConfig.Distributor,
                            DistributorHotelId = vdHotelInfo.DistributorHotelId,
                            DistributorRoomId = vdRoom.DistributorRoomId.NullToString(),
                            DistributorRoomId1 = vdRoom.DistributorRoomId1.NullToString(),
                            PushResult = vdRoom.PushResult,
                            PushStatus = vdRoom.PushStatus,
                            PushError = vdRoom.PushError,
                            UpdatedBy = updatedBy,
                            UpdatedByName = updatedByName,
                            UpdatedTime = DateTime.Now,
                        });
                    }
                }
            }

            return new ResultData<string> { Success = resultData.Success, Code = resultData.Code, Message = logText };
        }

        public async Task<ResultData<string>> QueryPushResult(int hotelId, int updatedBy, string updatedByName)
        {
            if (distributorConfig == null)
                throw new NullReferenceException("Config is null");

            var vdHotelInfo = await dbContext.Db.SqlQueryable<VDistributorHotel>(DistributorService.VDistributorHotelSQL).Where(e => e.Distributor.Equals(distributorConfig.Distributor) && e.HotelId.Equals(hotelId)).FirstAsync();
            if (vdHotelInfo == null)
                throw new NullReferenceException("VDistributorHotel is null");
            if(vdHotelInfo.PushResult == PushResult.NotPush || vdHotelInfo.PushStatus == PushStatus.NotPush)
                throw new NullReferenceException("VDistributorHotel is not pushed");

            var vdRoomList = await dbContext.Db.SqlQueryable<VDistributorRoom>(DistributorService.VDistributorRoomSQL).Where(e => e.Distributor.Equals(distributorConfig.Distributor) && e.HotelId.Equals(hotelId)).ToListAsync();
            var resultData = await idistributorPushApi.QueryPushResult(distributorConfig.Distributor, vdHotelInfo, vdRoomList);
            var logText = "";
            if (resultData.Success)
            {
                logText = $"查询推送结果 {distributorConfig.Distributor}";
                await logDbContext.UpdateLog.InsertAsync(new UpdateLog
                {
                    LogType = UpdateLogType.StaticData,
                    LogSubType = distributorConfig.Distributor,
                    LogText = logText,
                    LogLevel = LogLevel.Information,
                    BusinessId = hotelId.ToString(),
                    UpdatedBy = updatedBy,
                    UpdatedByName = updatedByName,
                    UpdatedTime = DateTime.Now,
                });
            }
            else
            {
                logText = $"查询推送结果失败 {distributorConfig.Distributor}: {resultData.Message}";
                await logDbContext.UpdateLog.InsertAsync(new UpdateLog
                {
                    LogType = UpdateLogType.StaticData,
                    LogSubType = distributorConfig.Distributor,
                    LogText = logText,
                    LogLevel = LogLevel.Error,
                    BusinessId = hotelId.ToString(),
                    UpdatedBy = updatedBy,
                    UpdatedByName = updatedByName,
                    UpdatedTime = DateTime.Now,
                });
            }

            if (resultData.Data != null)
            {
                if (resultData.Data.dHotel != null)
                {
                    await dbContext.DistributorHotel.UpdateAsync(e => new DistributorHotel
                    {
                        DistributorHotelId = resultData.Data.dHotel.DistributorHotelId,
                        DistributorHotelId1 = resultData.Data.dHotel.DistributorHotelId1,
                        PushResult = resultData.Data.dHotel.PushResult,
                        PushStatus = resultData.Data.dHotel.PushStatus,
                        PushError = resultData.Data.dHotel.PushError,
                        UpdatedBy = updatedBy,
                        UpdatedByName = updatedByName,
                        UpdatedTime = DateTime.Now,
                    },
                    e => e.Distributor.Equals(distributorConfig.Distributor) && e.HotelId.Equals(hotelId));
                }
                if (!resultData.Data.dRoomList.IsEmpty())
                {
                    foreach (var vdRoom in vdRoomList)
                    {
                        await dbContext.DistributorRoom.UpdateAsync(e => new DistributorRoom
                        {
                            DistributorRoomId = vdRoom.DistributorRoomId,
                            DistributorRoomId1 = vdRoom.DistributorRoomId1,
                            PushResult = vdRoom.PushResult,
                            PushStatus = vdRoom.PushStatus,
                            PushError = vdRoom.PushError,
                            UpdatedBy = updatedBy,
                            UpdatedByName = updatedByName,
                            UpdatedTime = DateTime.Now,
                        },
                        e => e.Distributor.Equals(distributorConfig.Distributor) && e.HotelId.Equals(hotelId) && e.RoomId.Equals(vdRoom.RoomId));
                    }
                }
            }

            return new ResultData<string> { Success = resultData.Success, Code = resultData.Code, Message = logText };
        }
    }
}
